package top.eggcode.template.log;

import com.alibaba.fastjson.JSON;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import top.eggcode.common.mvc.Result;
import top.eggcode.component.session.SessionHelper;
import top.eggcode.security.AuthTool;
import top.eggcode.spring.AsyncExecutor;
import top.eggcode.system.client.LogClientService;
import top.eggcode.system.client.model.ActionLog;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * Title: 操作记录
 * Description: 监控 controller 类的 public 方法
 * Date: 2021/5/24 23:06
 *
 * @author JiaQi Ding
 * @version 1.0
 */
@Aspect
@Component
@AllArgsConstructor
public class OperationRecordAspect {

    private final Logger logger = LogManager.getLogger(this.getClass());
    private final AsyncExecutor executor;
    private LogClientService logClientService;

    @Pointcut("execution(public * top.eggcode..*.control.*.* (..))")
    public void pointcut() {
    }

    @Around("pointcut()")
    public Object doAround(ProceedingJoinPoint point) throws Throwable {

        long beginTime = System.currentTimeMillis();
        Object result = null;
        try {
            result = point.proceed();
        } finally {
            long timeSpent = System.currentTimeMillis() - beginTime;

            // 接收到请求，记录请求内容
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            assert attributes != null;
            HttpServletRequest request = attributes.getRequest();

            String method = request.getMethod();
            String uri = request.getRequestURI();
            String queryString = request.getQueryString();

            Object[] args = point.getArgs();
            /* 针对 ServletRequest、ServletResponse 做特殊处理 */
            List<Object> userParams = Arrays.stream(args)
                    .filter(arg -> !(arg instanceof ServletRequest) && !(arg instanceof ServletResponse) && arg != null)
                    .collect(Collectors.toList());

            String params = JSON.toJSONString(userParams);

            String referrer = request.getHeader("Referer");
            String host = request.getHeader("Host");
            String responseBody = "";
            if (result instanceof Result) {
                responseBody = JSON.toJSONString(result);
            }
            String userAgent = request.getHeader("User-Agent");
            String username = "";
            Object usernameObj = SessionHelper.getSession().getAttribute("principal");
            if (usernameObj != null) {
                username = usernameObj.toString();
            }

            ActionLog log = new ActionLog();
            String classMethod = point.getSignature().getDeclaringTypeName() + "." + point.getSignature().getName();
            MethodSignature signature = (MethodSignature) point.getSignature();
            Method targetMethod = signature.getMethod();
            ApiOperation apiOperation = targetMethod.getAnnotation(ApiOperation.class);

            String name = "";
            if (apiOperation != null) {
                name = apiOperation.value();
            }

            Class<?> targetClass = point.getTarget().getClass();

            Api api = targetClass.getAnnotation(Api.class);
            String moduleName = "";

            if (Objects.nonNull(api)) {
                moduleName = api.value();
            }
            log.setMethod(method);
            log.setParams(params);
            log.setUri(uri);
            log.setTimeSpent(timeSpent);
            log.setUsername(username);
            log.setReferrer(referrer);
            log.setHost(host);
            log.setResult(responseBody);
            log.setUserAgent(userAgent);
            log.setSessionToken(request.getHeader("session-id"));
            log.setName(moduleName + "-" + name);
            log.setClassMethod(classMethod);
            executor.commit(() -> logClientService.saveToAction(log));
        }

        return result;
    }

}
